/*
 *      Author: Guannan Ma
 *      @mythmgn
 *      Whatever license applied
 */

#include "base.h"
/*
initStack

If successfully initilizing the stack, this would return 1
else return 0

*/

#define SIZE_OF_STACK_INCREMENT 7

static int stackIncrement[]= {
    128,
    256,
    1024,
    2048,
    4096,
    8192,
    16384
};
static int
nextStackSize(int numSlots) {
    int i=0;
    for (; i<SIZE_OF_STACK_INCREMENT; i++) {
        if (stackIncrement[i]>numSlots)
            return stackIncrement[i];
    }
	setErrStatusCode(REACH_MAX_SLOTS);
    return -1;
}

stack *
initStack(int slots,int autoExtend) {
    stack *s=(stack*)malloc(sizeof(stack));
    if( NULL == s ){
		setErrStatusCode(MEMORY_MALLOC_FAILURE);
        return NULL;
    }
    s->content=NULL;
    s->autoExtend=(autoExtend==1)?1:0;
    /*linkStack *ls;*/
    if (NULL != s->content)
        goto STACK_INIT_FAILURE;
    else {
        if ((s->content=(stackNode *)malloc((slots)*sizeof(stackNode)))==NULL) {
    		setErrStatusCode(MEMORY_MALLOC_FAILURE);
            goto STACK_INIT_FAILURE;
        }
        s->maxsize=slots;
        s->now=-1;
    }
    s->p_mlock = (struct mlock *) malloc(sizeof(struct mlock));
    if ( NULL == s->p_mlock ){
        setErrStatusCode(MEMORY_MALLOC_FAILURE);
        goto STACK_INIT_FAILURE;
    }
    if( 0 != mlock_init(s->p_mlock) ){
        setErrStatusCode(INIT_FAILURE);
        goto STACK_INIT_FAILURE;
    }
    return s;

STACK_INIT_FAILURE:
    freeStack(s);
    return NULL;
}

/*!
  push_in
            @arg p the pointer which points the object that will be inserted into the stack
            @arg s The stack container
  return:
            If s->now<s->maxsize-1,everything goes fine and it will return 1
            If s->now>=s->maxsize-1 and autoExtend is opened. It will return 1 and realloc mem for the stack.
            Otherwise, it will return 0. That means no slot available for the new item.
            If return -1, it means error occars.
*/
int
push_in(void *p,stack *s) {
    int rev = -1;
    ASSERT( NULL != s );
    mlock_lock(s->p_mlock);

    if (s->now<s->maxsize-1){
        /*Not full stack. Capble to push in a new item*/
        rev = 1;
    }
    else if (s->now==s->maxsize-1&&s->autoExtend==1) {
        int i;
        i=nextStackSize(s->maxsize);
        if (i<=-1){
            rev = -1;
        }
        s->content=(stackNode *)realloc(s->content,(s->maxsize+i)*sizeof(stackNode));
        if (!s->content){
    		setErrStatusCode(MEMORY_MALLOC_FAILURE);
            rev = -1;
        }
        else {
            /* expanded stack */
            s->maxsize+=i;
            rev = 1;
        }
    } else {
        rev = 0;
    }
    
    if ( 1 == rev ){
        (s->content+s->now+1)->value=p;
        (s->now)++;
    }
    mlock_unlock(s->p_mlock);
    return rev;
}

void *
pop_up(stack *s) {
	stackNode *p;
    if (isStackEmpty(s))
        return NULL;
    mlock_lock(s->p_mlock);
    s->now--;
    p=(s->content+s->now);
    p++;
    mlock_unlock(s->p_mlock);
    return p->value;
}

BOOL
isStackFull(stack *s) {
    BOOL rev = FALSE;
    ASSERT( NULL != s );
    mlock_lock(s->p_mlock);
    if (s->now==s->maxsize-1){
        rev = TRUE;
    }
    mlock_unlock(s->p_mlock);
    return rev;
}

BOOL
isStackEmpty(stack *s) {
    BOOL rev = FALSE;
    ASSERT( NULL != s );
    mlock_lock(s->p_mlock);
    if (s->now==-1){
        rev = TRUE;
    }
    mlock_unlock(s->p_mlock);
    return rev;
}

BOOL
freeStack(stack *s) {
    if ( NULL != s ){
        FREEUP(s->content);
        if( s->p_mlock ){
            mlock_destroy(s->p_mlock);
            FREEUP(s->p_mlock);
        }
        FREEUP(s);
    }
    return TRUE;
}
